<cfcomponent output="false">
	
	<!---  --->

	<cffunction name="connectToCalendar" access="public" output="false" returntype="struct">
		<cfargument name="code" required="true" />
		
		<cfset var local = {} />
		
		<cfsetting requestTimeOut="600" />

		<cfset local.result = $.run.it('googleAPIService.getRefreshToken',{code=arguments.code}) />

		<cfif local.result.success>
			<cfset local.expires = dateAdd('n',(local.result.expires_in / 61),now()) />
			<cfset local.access_token = "" />
			<cfset local.refresh_token = "" />
			<cfif structKeyExists(local.result,"access_token")>
				<cfset local.access_token = local.result.access_token />
			</cfif>
			<cfif structKeyExists(local.result,'refresh_token') and local.result.refresh_token neq "">
				<cfset local.refresh_token = local.result.refresh_token />
			</cfif>

			<cfset local.access_token_expire = dateTimeFormat(local.expires,"yyyy-mm-dd HH:nn:ss") />
			
			<!--- update user options with new calendar stuff --->
			<cfset $.run.it('optionService.saveOptions',{
				key="calendar",
				args={
					access_token=local.access_token,
					refresh_token=local.refresh_token,
					access_token_expire=local.access_token_expire
				}
			}) />
			
			<!--- sync with google --->
			<cfset $.run.it("googleService.fullSync") />
		</cfif>
		
		<cfreturn local.result />
		
	</cffunction>

	<!--- --->
	
	<cffunction name="disconnectCalendar" access="public" output="false" returntype="string">
		<cfargument name="refresh_token" required="true" />
		
		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
				
		<!--- try delete craftivity calendar (before revoke or cannot delete this calendar) --->
		<cfset local.success = $.run.it('googleService.deleteCraftivityCalendar') />
		<cfif not local.success>
			<cfreturn false />
		</cfif>

		<cfset local.result = $.run.it('googleAPIService.revokeRefreshToken',{refresh_token=arguments.refresh_token}) />
		
		<cfif local.result.success>
			<!--- remove all ties to google for craftivity events --->
			<cfset $.run.it('googleService.removeEventTiesToGoogle',{craftivity_calendar_id=local.options.craftivity_calendar_id}) />
			
			<!--- remove google calendar options --->
			<cfset $.run.it('optionService.saveOptions',{key="calendar",
				args={
					google_calendar_ids="",
					refresh_token="",
					access_token="",
					access_token_expire="",
					last_synced_on="",
					google_calendars="",
					initial_sync_complete="",
					pushThread="",
					pullThread=""
				}
			}) />
		</cfif>
		
		<cfreturn local.result.success />
		
	</cffunction>

	<!--- --->
	
	<cffunction name="removeEventTiesToGoogle" access="public" output="false" returntype="string">
		<cfargument name="craftivity_calendar_id" required="true" />
		
		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
						
		<!--- remove all ties to google for craftivity events --->
		<cfquery name="local.removeGoogleEvents" datasource="#$.site.getVar('datasource')#">
			update event
			set event.google_event_id = null, event.google_calendar_id = null
			where event.active = 1
			and event.user_id = #$.database.uuid(request.session.user.id)#
			and event.google_event_id is not null
			and event.google_calendar_id = #$.database.string(arguments.craftivity_calendar_id)#
		</cfquery>
		
		<!--- remove events previously tied to other google calendars --->
		<cfquery name="local.removeGoogleEvents" datasource="#$.site.getVar('datasource')#">
			update event
			set event.active = 0
			where event.active = 1
			and event.user_id = #$.database.uuid(request.session.user.id)#
			and event.google_event_id is not null
			and event.google_calendar_id != #$.database.string(arguments.craftivity_calendar_id)#
		</cfquery>
					
		<cfreturn true />
		
	</cffunction>

	<!--- --->
	
	<cffunction name="getAccessToken" access="public" output="false" returntype="string">
		
		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		<cfif local.options.refresh_token eq "">
			<cfreturn "" />
		</cfif>

		<!--- test for expired access token --->
		<cfif local.options.access_token_expire neq "" and dateCompare(now(),local.options.access_token_expire) gte 0>
			<cfset local.options.access_token = "" />
		</cfif>
		
		<!--- attempt to get an access token if none present --->
		<cfif local.options.access_token eq "">
			<cfset local.result = $.run.it('googleAPIService.requestAccessToken', {refresh_token=local.options.refresh_token}) />
			<cfif local.result.success>
				<cfset local.options = {} />
				<cfset local.options.access_token = local.result.access_token />
				<cfset local.options.access_token_expire = dateTimeFormat(dateAdd('n',(local.result.expires_in / 62),now()),"yyyy-mm-dd HH:nn:ss") />
				<cfset $.run.it('optionService.saveOptions',{key="calendar",args=local.options}) />
			</cfif>
		</cfif>

		<cfreturn local.options.access_token />
		
	</cffunction>

	<!--- --->
	
	<cffunction name="createCraftivityCalendar" access="public" output="false" returntype="string">
		
		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		<cfset local.result = $.run.it('googleAPIService.createCalendar',{summary="Craftivity"}) />
		
		<cfif local.result.success>
			<cfset local.options.google_calendars[local.result.id] = {name='Craftivity',nextSyncToken=""} />
			<cfset $.run.it('optionService.saveOptions',{key='calendar',args={
				craftivity_calendar_id=local.result.id,
				google_calendars=local.options.google_calendars
			}}) />
		</cfif>
		
		<cfreturn local.result.success />
				
	</cffunction>

	<!--- --->
	
	<cffunction name="deleteCraftivityCalendar" access="public" output="true" returntype="string">
		
		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		
		<cfif local.options.craftivity_calendar_id neq ''>
			<cfset local.result = $.run.it('googleAPIService.deleteCalendar',{
				googleCalendarID=local.options.craftivity_calendar_id
			}) />
			<cfif local.result.success>
				<cfset $.run.it('optionService.saveOptions',{key='calendar',args={craftivity_calendar_id=""}}) />
			</cfif>
			<cfreturn local.result.success />
		</cfif>
		
		<cfreturn true />
				
	</cffunction>

	<!--- --->
	
	<cffunction name="getCalendars" access="public" output="false" returntype="struct">
		
		<cfset var local = {} />
		
		<cfset local.calendars = {} />
		
		<cfset local.result = $.run.it('googleAPIService.getCalendarList') />
		<cfset local.options = $.run.it('optionService.getOptions',{key="calendar"}) />

		<cfif local.result.success>
			<cfloop array="#local.result.items#" item="local.item" index="local.i">
				<!--- ignore these calendars --->
				<cfif local.item.id neq '##contacts@group.v.calendar.google.com' and not local.item.id contains '##holiday@group.v.calendar.google.com'	and not local.item.id contains '##weather@group.v.calendar.google.com' and not local.item.id contains '##weeknum@group.v.calendar.google.com'>
					<!--- only allow 1 Craftivity calendar --->
					<cfif local.item.summary eq "Craftivity" and local.item.id neq local.options.craftivity_calendar_id>
						<cfset $.run.it('googleAPIService.deleteCalendar',{googleCalendarID=local.item.id}) />
					<cfelse>
						<cfset local.calendars[local.item.id] = {name=local.item.summary,nextSyncToken=""} />
					</cfif>
				</cfif>
			</cfloop>
		</cfif>
		
		<cfreturn local.calendars />
				
	</cffunction>
	
	<!--- --->
	
	<cffunction name="getUserCalendars" access="public" output="false" returntype="string">
		
		<cfset var local = {} />
		
		<cfset local.options = $.site.getOptions('calendar') />

		<cfreturn local.options.google_calendar_ids />
						
	</cffunction>
	
	<!--- --->
	
	<cffunction name="updateGoogleEvent" access="public" output="false" returntype="string">
		<cfargument name="googleEventID" required="true" />
		<cfargument name="googleCalendarID" required="true" />
		<cfargument name="description" required="false" default="" />
		<cfargument name="starttime" required="true" />
		<cfargument name="stoptime" required="false" default="00:00:00" />
		<cfargument name="category" required="false" default="0" />
		
		<cfset var local = {} />
		
		<cfset local.colorID = getGoogleColorByCategory(arguments.category) />
		<cfset local.start = arguments.starttime />
		<cfset local.stop = arguments.stoptime />
		<cfset local.sequence = 2 />
		<cfset local.event = $.run.it('googleAPIService.getEvent',{
			calendarID=arguments.googleCalendarID,
			eventID=arguments.googleEventID,
			args={fields="sequence"}		
		}) />
		<cfif local.event.success>
			<cfset local.sequence = local.event.sequence + 1 />
		</cfif>
		
		<cfset local.gstart = {dateTime=dateFormat(local.start,'yyyy-mm-dd') & 'T' & timeFormat(local.start,'HH:mm:ss') & 'Z',timezone="UTC"} />
		<cfset local.gend = {dateTime=dateFormat(local.stop,'yyyy-mm-dd') & 'T' & timeFormat(local.stop,'HH:mm:ss') & 'Z',timeZone="UTC"} />
		
		<!--- test for allday --->
		<cfif timeFormat($.date.applyLocalization(local.start),'HH:mm:ss') eq '00:00:00' and timeFormat($.date.applyLocalization(local.stop),'HH:mm:ss') eq '23:55:00'>
			<cfset local.gstart = {date=dateFormat(local.start,'yyyy-mm-dd'),timezone="UTC"} />
			<cfset local.gend = {date=dateFormat(dateAdd('d',1,local.start),'yyyy-mm-dd'),timeZone="UTC"} />
		</cfif>
		
		<!--- setup the arguments --->
		<cfset local.args = {
			summary=arguments.description,
			start=local.gstart,
			end=local.gend,
			colorId=local.colorID,
			sequence=local.sequence
		} />
		
		<cfset local.result = $.run.it('googleAPIService.updateEvent',{
			calendarID=arguments.googleCalendarID,
			eventID=arguments.googleEventID,
			args=local.args
		}) />
		
		<cfreturn true />

	</cffunction>
	
	<!--- --->
	
	<cffunction name="insertGoogleEvent" access="public" output="false" returntype="struct">
		<cfargument name="googleCalendarID" required="true" />
		<cfargument name="description" required="false" default="" />
		<cfargument name="starttime" required="true" />
		<cfargument name="stoptime" required="false" default="00:00:00" />
		<cfargument name="category" required="false" default="0" />
		<cfargument name="eventID" required="true" />

		<cfset var local = {} />
		
		<cfset local.options = $.site.getOptions(key='calendar') />
		<cfset local.colorID = getGoogleColorByCategory(arguments.category) />
		<cfset local.start = arguments.starttime />
		<cfset local.stop = arguments.stoptime />
		
		<cfif arguments.googleCalendarID eq "">
			<cfset arguments.googleCalendarID = local.options.craftivity_calendar_id />
		</cfif>
		
		<cfif local.options.craftivity_calendar_id eq "">
			<cfreturn {success=false} />
		</cfif>
		
		<cfset local.gstart = {dateTime=dateFormat(local.start,'yyyy-mm-dd') & 'T' & timeFormat(local.start,'HH:mm:ss') & 'Z',timezone="UTC"} />
		<cfset local.gend = {dateTime=dateFormat(local.stop,'yyyy-mm-dd') & 'T' & timeFormat(local.stop,'HH:mm:ss') & 'Z',timeZone="UTC"} />
		
		<!--- test for allday --->
		<cfif timeFormat($.date.applyLocalization(local.start),'HH:mm:ss') eq '00:00:00' and timeFormat($.date.applyLocalization(local.stop),'HH:mm:ss') eq '23:55:00'>
			<cfset local.gstart = {date=dateFormat(local.start,'yyyy-mm-dd'),timezone="UTC"} />
			<cfset local.gend = {date=dateFormat(dateAdd('d',1,local.start),'yyyy-mm-dd'),timeZone="UTC"} />
		</cfif>
		
		<!--- setup the arguments --->
		<cfset local.args = {
			summary=arguments.description,
			start=local.gstart,
			end=local.gend,
			colorId=local.colorID
		} />
		
		<cfset local.result = $.run.it('googleAPIService.insertEvent',{
			calendarID=arguments.googleCalendarID,
			args=local.args		
		}) />
		
		<!--- update event with new google event id --->
		<cfif local.result.success>
			<cfset $.database.save(
				table="event",
				id=arguments.eventID,
				google_calendar_id=arguments.googleCalendarID,
				google_event_id=local.result.id
			) />
		</cfif>
		
		<cfreturn local.result />

	</cffunction>
	
	<!--- --->
	
	<cffunction name="getEventsByCalendarID" access="public" output="false" returntype="struct">
		<cfargument name="calendarID" required="true" />
		<cfargument name="maxResults" required="false" default="250" />
		<cfargument name="singleEvent" required="false" default="false" />
		<cfargument name="showDeleted" required="false" default="true" />
		<cfargument name="nextPageToken" required="false" default="" />
		<cfargument name="nextSyncToken" required="false" default="" />

		<cfset var local = {} />
		
		<cfset local.fields = "accessRole,etag,items(colorId,created,description,end,endTimeUnspecified,htmlLink,id,kind,originalStartTime,recurrence,recurringEventId,start,status,summary,updated),kind,summary,timeZone,nextPageToken,nextSyncToken" />
		
		<cfset local.args = {
			maxResults=arguments.maxResults,
			singleEvents=arguments.singleEvent,
			timeZone="+00:00",
			fields=local.fields,
			showDeleted=arguments.showDeleted
		} />
		
		<cfif arguments.nextPageToken neq "">
			<cfset local.args.pageToken = arguments.nextPageToken />
		</cfif>
		
		<cfif arguments.nextSyncToken neq "">
			<cfset local.args.syncToken = arguments.nextSyncToken />
		</cfif>
		
		<cfset local.result = $.run.it('googleAPIService.getEvents',{
			calendarID=arguments.calendarID,
			args=local.args
		}) />
		
		<cfset local.package = {
			items=[],
			nextSyncToken="",
			nextPageToken="",
			success="false",
			fullSyncRequired=""
		} />

		<cfif local.result.success>
			<cfif structKeyExists(local.result,"items")>
				<cfset local.package.items = local.result.items />
			<cfelse>
				<cfset local.package.items = [] />
			</cfif>
			<cfset local.package.success = true />
			<cfif structKeyExists(local.result,"nextSyncToken")>
				<cfset local.package.nextSyncToken = local.result.nextSyncToken />
			</cfif>
			<cfif structKeyExists(local.result,"nextPageToken")>
				<cfset local.package.nextPageToken = local.result.nextPageToken />
			</cfif>
			<cfif structKeyExists(local.result,"status_code") and local.result.status_code eq "410">
				<cfset local.package.fullSyncRequired="true" />
			</cfif>
		</cfif>
		
		<cfreturn local.package />
		
	</cffunction>
	
	<!--- --->
	
	<cffunction name="getGoogleColorByCategory" access="public" output="false" returntype="string">
		<cfargument name="category" required="true" />
		
		<cfset var local = {} />
		
		<cfset local.result = 8 />
		
		<cfset local.categoryToColor = {
			cat0=8,
			cat1=1,
			cat2=4,
			cat3=10,
			cat4=5,
			cat5=3,
			cat6=7
		} />
		
		<cfif structKeyExists(local.categoryToColor,'cat#arguments.category#')>
			<cfset local.result = local.categoryToColor['cat#arguments.category#'] />
		</cfif>
		
		<cfreturn local.result />

	</cffunction>
	
	<!--- --->
	
	<cffunction name="formatEventArray" access="public" output="false" returntype="struct">
		<cfargument name="array" required="true" />
		<cfargument name="googleCalendarID" required="true" />
		
		<cfset var local = {} />
		
		<cfset local.events = {} />
		
		<cfloop array="#arguments.array#" index="local.j" item="local.item">
			<cfif structKeyExists(local.item,'created')>
				<cfset local.link = "" />
				<cfif structKeyExists(local.item,'htmlLink')>
					<cfset local.link = local.item.htmlLink />
				</cfif>
				<cfset local.event = {
					id:local.item.id,
					created:formatGoogleDate(local.item.created),
					updated:formatGoogleDate(local.item.updated),
					starttime:"00:00:00",
					date:"",
					stopdate:"",
					status:local.item.status,
					summary:"No Title",
					stoptime:"23:59:59",
					recurrence:[],
					google_calendar_link:local.link,
					google_calendar_id:arguments.googleCalendarID,
					status:local.item.status
				} />
				<cfif structKeyExists(local.item,"summary")>
					<cfset local.event.summary = local.item.summary />
				</cfif>
				<cfif structKeyExists(local.item,"recurrence")>
					<cfset local.event.recurrence = local.item.recurrence />
				</cfif>
				
				<!--- get the date --->
				<cfif structKeyExists(local.item.start,"datetime")>
					<cfset local.google_date = formatGoogleDate(local.item.start.dateTime) />
					<!--- apply localization --->
					<cfset local.start = $.date.applyLocalization(local.google_date) />
					
					<cfset local.event.date = dateFormat(local.start,'mm/dd/yyyy') />
					<!--- set the end date, will be overwritten later if necessary --->
					<cfset local.event.stopdate = local.event.date />
					<cfset local.event.starttime = timeFormat(local.start,'HH:mm:ss') />
				<cfelseif structKeyExists(local.item.start,"date")>
					<cfset local.event.date = dateFormat(local.item.start.date,'mm/dd/yyyy') />
					<!--- set the end date, will be overwritten later if necessary --->
					<cfset local.event.stopdate = local.event.date />
					<cfset local.event.starttime = '00:00:00' />
				</cfif>
	
				<!--- get the stop --->
				<cfif structKeyExists(local.item.end,"datetime")>
					<cfset local.google_date = formatGoogleDate(local.item.end.dateTime) />
					<!--- apply localization --->
					<cfset local.end = $.date.applyLocalization(local.google_date) />
					<cfset local.event.stopdate = dateFormat(local.end,'mm/dd/yyyy') />
					<cfset local.event.stoptime = timeFormat(local.end,'HH:mm:ss') /> />
				<cfelseif structKeyExists(local.item.end,"date")>
					<cfset local.event.stopdate = dateFormat(local.item.end.date,'mm/dd/yyyy') />
					<!--- bring the stopdate back one, since google's all day event is start:2014-01-01 end:2014-01-02 --->
					<cfset local.event.stopdate = dateFormat(dateAdd('d',-1,local.event.stopdate),'mm/dd/yyyy') />
					<cfset local.event.stoptime = "23:55:00" />
				</cfif>
				
				<cfset local.events[local.item.id] = local.event />
			</cfif>
		</cfloop>

		<cfreturn local.events />

	</cffunction>
	
	<!--- --->
	
	<cffunction name="formatCraftivityGoogleEvents" access="public" output="false" returntype="struct">
		<cfargument name="query" required="true" />

		<cfset var local = {} />
		
		<cfset local.result = {} />
		
		<cfloop query="arguments.query">
			<cfset local.localstart = $.date.applyLocalization('#arguments.query.start_date# #arguments.query.start_time#') />
			<cfset local.localstop = $.date.applyLocalization('#arguments.query.stop_date# #arguments.query.stop_time#') />

			<cfset local.thisEvent = {} />
			<cfset local.thisEvent.id = arguments.query.id />
			<cfset local.thisEvent.description = arguments.query.description />
			<cfset local.thisEvent.start_date = left(local.localstart,10) />
			<cfset local.thisEvent.start_time = right(local.localstart,8) />
			<cfset local.thisEvent.stop_date = left(local.localstop,10) />
			<cfset local.thisEvent.stop_time = right(local.localstop,8) />
			<cfset local.thisEvent.category = arguments.query.category />
			<cfset local.thisEvent.google_event_id = arguments.query.google_event_id />
			<cfset local.thisEvent.google_calendar_id = arguments.query.google_calendar_id />
			<cfset local.thisEvent.exists = "" />
			
			<cfset local.result[arguments.query.google_event_id] = local.thisEvent />
		</cfloop>
		
		<!--- add blank reference event --->
		<cfset local.result.blank = {
			id="",
			description="",
			start_date="",
			start_time="",
			stop_date="",
			stop_time="",
			category="",
			google_event_id="",
			google_calendar_id="",
			exists=""
		} />

		<cfreturn local.result />

	</cffunction>
	
	<!--- --->
	
	<cffunction name="formatGoogleDate" access="public" output="false" returntype="string">
		<cfargument name="gdate" required="true" />
		<!--- google dates come in 2 flavors yyyy-mm-ddT00:00:00Z or yyyy-mm-ddT00:00:00-06:00 --->
		
		<cfset var local = {} />
		
		<!--- cleanse the date if it is year 0000 --->
		<cfif left(arguments.gdate,4) eq "0000">
			<cfset arguments.gdate = rereplace(arguments.gDate,'0000-','1900-') />
		</cfif>
		
		<cfset local.date = listGetAt(arguments.gdate,1,'T') />
		<cfset local.rightHalf = listGetAt(arguments.gdate,2,'T') />
		<cfset local.time = left(local.rightHalf,8) />
		<cfset local.timezone = right(local.rightHalf,len(local.rightHalf)-8) />
		
		<!--- alter timezone for high precision RFC 3339 (2014-04-21T18:31:28.690Z) --->
		<cfif left(local.timezone,1) eq ".">
			<cfset local.timezone = right(local.timezone,len(local.timezone)-4) />
		</cfif>
		
		<cfset local.offset = 0 />
		
		<cfif not local.timezone contains "Z">
			<cfset local.offset = listGetAt(local.timezone,1,':') + 0 />
		</cfif>
		
		<!--- adjust for timezone --->
		<cfset local.datetime = datetimeFormat(dateAdd('h',local.offset*-1,'#local.date# #local.time#'),'mm/dd/yyyy HH:nn:ss') />

		<cfreturn local.dateTime />

	</cffunction>

	<!--- ^ Evaluated and reworked functions ^ --->
	
	<cffunction name="checkSyncCalendar" access="public" output="false" returntype="void">
		<cfargument name="refresh" required="false" default="false" />
		
		<cfset var local = {} />
		
		<cfsetting requestTimeOut="600" />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />

		<!--- test for expired access token --->
		<cfif local.options.access_token_expire neq "" and dateCompare(now(),local.options.access_token_expire) gte 0>
			<cfset arguments.refresh = "true" />
		<cfelseif local.options.last_synced_on neq '' and dateDiff('n',local.options.last_synced_on,now()) gt 15>
			<cfset arguments.refresh = "true" />
		</cfif>
		
		<!--- call update functions if they don't exist' --->
		<cfif arguments.refresh eq true>
			<cfset $.run.thread('googleService.syncCalendar') />>
		</cfif>
		
		<cfreturn />
		
	</cffunction>

	<!--- --->

	<cffunction name="syncCalendar" access="public" output="false" returntype="void">
		<cfargument name="threaded" required="false" default="true" />
		<!--- pulls any events that may have created outside of craftivity after last_synced_on --->
				
		<cfset var local = {} />
		
		<cfif not arguments.threaded>
			<cfset local.thread.name = "" />
		</cfif>
		
		<cfset local.options= $.run.it('optionService.saveOptions',{key='calendar',args={
			pullThread=thread.name
		}}) />
		
		<cftry>
			<!--- get options --->
			<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
					
			<!--- get list of google calendars to sync with --->
			<cfset local.calendarIDs = $.run.it("googleService.getUserCalendars") />
	
			<!--- see if any calendars need full sync --->
			<cfset local.fullSyncCalendarIDs = "" />
			
			<!--- loop through the google cals --->
			<cfloop list="#local.calendarIDs#" index="local.i">
				<cfif not local.options.initial_sync_complete>
					<!--- do nothing cause my mind can't do conditionals atm--->
				<cfelse>
					<cfset local.package = $.run.it('googleService.syncCalendarByCalendarID',{
						calendarID=local.i
					}) />
					
					<!--- update the sync token --->
					<cfif local.package.nextSyncToken neq "">
						<cfset local.options.google_calendars[local.i].nextSyncToken = local.package.nextSyncToken />
						<cfset local.options = $.run.it('optionService.saveOptions',{key='calendar',args={
							google_calendars=local.options.google_calendars}
						}) />	
					</cfif>
					
					<!--- check the package to see if full sync is required --->
					<cfif local.package.fullSyncRequired eq true>
						<cfset local.fullSyncCalendarIDs = listAppend(local.fullSyncCalendarIDs,local.i) />
					</cfif>
				</cfif>
			</cfloop>
			
			<!--- update options to indicate the calendar was updated --->
			<cfset $.run.it('optionService.saveOptions',{key="calendar",args={
				last_synced_on=datetimeFormat(now(),'yyyy-mm-dd HH:nn:ss')
			}}) />
			
			<cfif listLen(local.fullSyncCalendarIDs) gt 0>
				<cfset $.run.it('googleService.fullSync') />
			</cfif>
			
			<cfset local.options= $.run.it('optionService.saveOptions',{key='calendar',args={
				pullThread=""
			}}) />
		
			<cfcatch>
				<cfset local.options= $.run.it('optionService.saveOptions',{key='calendar',args={
					pullThread="Error"
				}}) />
			</cfcatch>
		</cftry>	
			
	</cffunction>

	<!--- --->
	
	<cffunction name="syncCalendarByCalendarID" access="public" output="false" returntype="struct">
		<cfargument name="calendarID" required="true" />
				
		<cfset var local = {} />
		
		<!--- get options --->
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		
		<!--- this is the struct of google events for the timespan --->
		<cfset local.googleEvents = {} />
		
		<!--- for users without nextSyncToken (Old options) --->
		<cfset local.nextSyncToken = "" />
		<cfif structKeyExists(local.options.google_calendars[arguments.calendarID],"nextSyncToken")>
			<cfset local.nextSyncToken = local.options.google_calendars[arguments.calendarID].nextSyncToken />
		</cfif>
		
		<cfset local.package = $.run.it('googleService.getEventsByCalendarID',{	
			calendarID=arguments.calendarID,
			nextSyncToken=local.nextSyncToken
		})/>
		
		<cfif local.package.fullSyncRequired neq "true">
			<!--- update the nextSyncToken for this package --->
			<cfif structKeyExists(local.package,"nextSyncToken")>
				<cfset local.options.google_calendars[arguments.calendarID].nextSyncToken = local.package.nextSyncToken />
				<cfset local.options = $.run.it('optionService.saveOptions',{key='calendar',args={
					google_calendars=local.options.google_calendars}
				}) />
			</cfif>
			
			<cfset local.googleEvents = $.run.it('googleService.formatEventArray',{
				array=local.package.items,
				googleCalendarID=arguments.calendarID
			}) />
	
			<!--- loop through the google events --->
			<cfloop collection="#local.googleEvents#" item="local.item">
				<cfset local.thisEvent = local.googleEvents[local.item] />

				<!--- delete update or create? --->
				<cfif local.thisEvent.status eq "cancelled">
					<!--- delete all events with this googleEventID--->
					<cfset local.eventsByGID = $.run.it('calendarService.getEvents',{googleEventIDLike=local.item}) />
					<cfloop query="local.eventsByGID">
						<cfset $.database.delete(
							id=local.eventsByGID.id,
							table="event"
						) />
					</cfloop>						
				<cfelse>
					<!--- begin by removing all child events, for instance, if a previously pulled multiday is updated to be single --->
					<cfset local.childEventsByGID = $.run.it('calendarService.getEvents',{googleEventIDLike=local.item,isChild=true}) />
					<cfloop query="local.childEventsByGID">
						<cfset $.run.it('calendarService.deleteEVent',{eventID=local.childEventsByGID.id}) />
					</cfloop>
					
					<!--- test for recurring event --->
					<cfif structKeyExists(local.thisEvent,'recurrence') and arrayLen(local.thisEvent.recurrence) gt 0>
						<cfset local.id = $.run.it('googleService.saveRecurringEvent',{
							event=local.thisEvent		
						}) />
					<cfelseif local.thisEvent.date neq local.thisEvent.stopdate>
						<!--- multiday event save --->
						<cfset local.id = $.run.it('googleService.saveMultidayEvent',{
							event=local.thisEvent,
							calendarID=arguments.calendarID,
							googleEventID=local.item
						}) />
					<cfelse>
						<cfset local.eventByGID = $.run.it('calendarService.getEvents',{googleEventID=local.item}) />

						<!--- create --->						
						<cfset local.id = $.run.it('calendarService.saveEvent',{
							id=local.eventByGID.id,
							description=local.thisEvent.summary,
							start=local.thisEvent.starttime,
							stop=local.thisEvent.stoptime,
							date=local.thisEvent.date,
							category=local.eventByGID.category,
							google_calendar_id=arguments.calendarID,
							google_event_id=local.thisEvent.id,
							google_calendar_link=local.thisEvent.google_calendar_link,
							ignore_google="true"
						}) />
					</cfif>
				</cfif>
			</cfloop>
		</cfif>
		
		<cfreturn local.package />
				
	</cffunction>

	<!--- --->
	
	<cffunction name="updateCalendarSelected" access="public" output="false" returntype="string">
		<cfargument name="oldIDs" required="true" />
		<cfargument name="newIDs" required="true" />
		
		<cfset var local = {} />
		
		<!--- detect removed ids --->
		<cfloop list="#arguments.oldIDs#" index="local.i">
			<cfif not arguments.newIDs contains local.i>
				<!--- remove old calendar events --->
				<cfquery name="local.removeEventsByGoogleCalendarID" datasource="#$.site.getVar('datasource')#">
					update event
					set active = 0
					where event.active = 1
					and event.user_id = #$.database.uuid(request.session.user.id)#
					and event.google_calendar_id = #$.database.string(local.i)#
				</cfquery>
			</cfif>
		</cfloop>
		
		<!--- detect new ids --->
		<cfloop list="#arguments.newIDs#" index="local.i">
			<cfif not arguments.oldIDs contains local.i>
				<!--- TODO: update this call --->
				<cfset $.run.it('googleService._pullAllEvents',{googleCalendarIds=local.i,threaded=false}) />
			</cfif>
		</cfloop>
		
		<cfreturn true />

	</cffunction>

	<!--- --->
	
	<cffunction name="_pushEvents" access="public" output="false" returntype="string">
		<cfargument name="daysBefore" required="false" default="-180" />
		<cfargument name="daysAfter" required="false" default="730" />
		<cfargument name="per" required="false" default="10" />
		<cfargument name="iteration" required="false" default="1" />
		<cfargument name="maxIteration" required="false" default="8" />
		<cfargument name="threaded" required="false" default="true" />

		<cfset var local = {} />
		
		<cfsetting requesttimeout = "600">
		
		<cfif not arguments.threaded>
			<cfset local.thread.name = "" />
		</cfif>
		
		<cfset local.today = $.date.getDate() />
		<cfset local.startdate = dateFormat(dateAdd('d',arguments.daysBefore,local.today),'mm/dd/yyyy') />
		<cfset local.stopdate = dateFormat(dateAdd('d',arguments.daysAfter,local.today),'mm/dd/yyyy') />
		
		<cfset local.events = $.run.it(method='calendarService.getEvents',args={startdate=local.startdate,enddate=local.stopdate,isGoogle="false",userID=request.session.user.id}) />
		
		<!--- indicate which thread is helping this user --->
		<cfset $.run.it(method='optionService.saveOptions',args={key='calendar',args={pushThread=thread.name}}) />
		
		<cfset local.options = $.site.getOptions('calendar') />

		<cftry>
			<cfloop query="local.events">
				<cfif local.events.google_event_id eq "" and local.events.currentRow lt 75> <!--- will recurse on self. 75 is limit for 1 cycle --->
					<cfset sleep(300) />
					<cfset local.result = $.run.it(method='googleService.insertGoogleEvent',args={
						googleCalendarID=local.options.craftivity_calendar_id,
						description=local.events.description,
						starttime=local.events.start_date & ' ' & local.events.start_time,
						stoptime=local.events.stop_date & ' ' & local.events.stop_time,
						category=local.events.category,
						eventID=local.events.id,
						options=local.options
					}) />
				</cfif>
			</cfloop>
			
			<!--- check to see if all events updated --->
			<cfset local.events = $.run.it(method='calendarService.getEvents',args={startdate=local.startdate,enddate=local.stopdate,isGoogle="false",userID=request.session.user.id}) />
			<cfif local.events.recordcount gt 0 and arguments.iteration lte arguments.maxIteration>
				<cfset arguments.iteration ++ />
				<cfreturn $.run.it(method='googleService._pushEvents',args=arguments) />
			<cfelse>
				<cfset $.run.it(method='optionService.saveOptions',args={key='calendar',args={initial_sync_complete="true",last_synced_on=dateTimeFormat(now(),'mm/dd/yyyy HH:nn:ss')}}) />
			</cfif>
			
			<cfcatch>
				<!--- if something fails in the thread, update last_synced on anyways to prevent created events from being picked up as new re-inserted into google --->
				<cfset $.run.it(method='optionService.saveOptions',args={key='calendar',args={initial_sync_complete="true",last_synced_on=dateTimeFormat(now(),'mm/dd/yyyy HH:nn:ss')}}) />
				<cfrethrow />
			</cfcatch>
		</cftry>
		
		<!--- turn off the pushThread since thread completes here --->
		<cfset $.run.it(method='optionService.saveOptions',args={key='calendar',args={pushThread="",last_synced_on=datetimeFormat(now(),'yyyy-mm-dd HH:nn:ss')}}) />

		<cfreturn true />

	</cffunction>

	<!--- --->
	
	<cffunction name="saveMultidayEvent" access="public" output="false" returntype="string">
		<cfargument name="event" required="true" />
		<cfargument name="calendarID" required="true" />
		<cfargument name="googleEventID" required="true" />
		<cfargument name="parent_id" required="false" default="" />
		<cfargument name="clear_events" required="false" default="true" />

		<cfset var local = {} />
		
		<cfset local.options = $.site.getOptions('calendar') />
		
		<cfset local.category = 0 />
		<!--- get existing events + record category--->
		<cfif arguments.clear_events>
			<cfset local.eventByGID = $.run.it('calendarService.getEvents',{googleEventID=arguments.googleEventID,userID=request.session.user.id}) />
			<cfset local.category = local.eventByGID.category />
			<!--- delete existing craftivity events --->
			<cfif local.eventByGID.recordcount gt 0>
				<cfloop query="local.eventByGID">
					<!--- don't pass in google_event_id, that will cause the delete function to seek and remove the google_event as well  --->
					<cfset $.run.it('calendarService.deleteEvent',{eventID=local.eventByGID.id}) />
				</cfloop>
			</cfif>
		</cfif>
		
		<!--- * at this point, craftivity has no events tied to this google event --->
		<!--- create 1 parent, and more child events tied to this googleEventID but push nothing back to google --->
		
		<!--- save new events --->
		<cfset local.startdate = arguments.event.date />
		<cfset local.nextdate = local.startdate />
		<cfset local.parentID = arguments.parent_id />
		<cfloop from="1" to="92" index="local.i"> <!--- using index of 92 to prevent hanging --->
			<cfset local.nextdate = dateFormat(dateAdd('d',1,local.nextdate),'mm/dd/yyyy') />
			<cfset local.eventArgs = { <!--- needs START and STOP --->
				id:"",
				description:arguments.event.summary,
				category:local.category,
				date:local.startdate,
				google_calendar_id:arguments.event.google_calendar_id,
				google_event_id:arguments.googleEventID,
				google_calendar_link:arguments.event.google_calendar_link,
				ignore_google:true,
				parentID:local.parentID
			} />

			<!--- starttime --->
			<cfif local.i eq 1> <!--- first event in series uses event.starttime, all others will use 00:00:00 --->
				<cfset local.eventArgs.start = arguments.event.starttime />
			<cfelse>
				<cfset local.eventArgs.start = "00:00:00" />
			</cfif>
			
			<!--- stoptime --->
			<cfif local.startdate eq arguments.event.stopdate> <!--- last event in series uses stoptime, all others will use 23:55:00 --->
				<cfset local.eventArgs.stop = arguments.event.stoptime />
			<cfelse>
				<cfset local.eventArgs.stop = "23:55:00" />
			</cfif>
			
			<cfset local.id = $.run.it(method='calendarService.saveEvent',args=local.eventArgs) />
			<cfif local.i eq 1 and local.parentID eq ""> <!--- use the passed in parentID if available --->
				<!--- all subsequent events use initial event's id as parentID --->
				<cfset local.parentID = local.id />
			</cfif>
			
			<cfif local.startdate eq arguments.event.stopdate>
				<cfbreak />
			</cfif>
			
			<!--- increment startdate --->
			<cfset local.startdate = dateFormat(dateAdd('d',1,local.startdate),'mm/dd/yyyy') />
		</cfloop>

		<cfreturn local.parentID />

	</cffunction>

	<!--- --->
	
	<cffunction name="saveRecurringEvent" access="public" output="false" returntype="string">
		<cfargument name="event" />

		<cfset var local = {} />
				
		<!--- start by removing any existing craftivity events --->
		<cfset local.eventByGID = $.run.it('calendarService.getEvents',{googleEventIDLike=arguments.event.id,userID=request.session.user.id}) />
		<!--- delete existing craftivity events --->
		<cfif local.eventByGID.recordcount gt 0>
			<cfloop query="local.eventByGID">
				<!--- don't pass in google_event_id, that will cause the delete function to seek and remove the google_event as well  --->
				<cfset $.run.it('calendarService.deleteEvent',{eventID=local.eventByGID.id}) />
			</cfloop>
		</cfif>		
		
		<cfset local.result = $.run.it('googleAPIService.getEventInstances',{calendarID=arguments.event.google_calendar_id,eventID=arguments.event.id}) />

		<cfif local.result.success>
			<cfset local.formattedGoogleEvents = $.run.it(method='googleService.formatEventArray',args={
				array=local.result.items,
				googleCalendarID=arguments.event.google_calendar_id
			}) />
			
			<!--- save the parent event --->
			<cfif arguments.event.date neq arguments.event.stopdate>
				<cfset local.parent_id = $.run.it(method='googleService.saveMultidayEvent',args={
					event=arguments.event,
					calendarID=arguments.event.google_calendar_id,
					googleEventID=arguments.event.id
				}) />
			<cfelse>
				<cfset local.parent_id = $.run.it(method='calendarService.saveEvent',args={
					id="",
					description=arguments.event.summary,
					start=arguments.event.starttime,
					stop=arguments.event.stoptime,
					date=arguments.event.date,
					category=0,
					google_calendar_id=arguments.event.google_calendar_id,
					google_event_id=arguments.event.id,
					google_calendar_link=arguments.event.google_calendar_link,
					ignore_google=true
				}) />
			</cfif>
			
			<!--- loop through results and save events --->
			<cfloop collection="#local.formattedGoogleEvents#" index="local.googleEventID" item="local.googleEvent">
				<cfif arguments.event.date eq local.googleEvent.date and arguments.event.starttime eq local.googleEvent.starttime>
					<!--- this is the parent event, already saved --->
				<cfelse>
					<!--- test for multiday event --->
					<cfif local.googleEvent.date neq local.googleEvent.stopdate>
						<cfset local.id = $.run.it(method='googleService.saveMultidayEvent',args={
							event=local.googleEvent,
							calendarID=arguments.event.google_calendar_id,
							googleEventID=local.googleEvent.id,
							parent_id=local.parent_id,
							clear_events=false
						}) />
					<cfelse>
						<cfset local.id = $.run.it(method='calendarService.saveEvent',args={
							id="",
							description=local.googleEvent.summary,
							start=local.googleEvent.starttime,
							stop=local.googleEvent.stoptime,
							date=local.googleEvent.date,
							category=0,
							google_calendar_id=arguments.event.google_calendar_id,
							google_event_id=local.googleEvent.id,
							google_calendar_link=arguments.event.google_calendar_link,
							ignore_google=true,
							parentID=local.parent_id
						}) />
					</cfif>
				</cfif>
			</cfloop>
		</cfif>
		
		<cfreturn true />

	</cffunction>

	<!--- --->
	
	<cffunction name="fullSync" access="public" output="false" returntype="string">

		<cfsetting requestTimeOut="600" />

		<cfset var local = {} />
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		
		<!--- remove event ties to google --->
		<cfset $.run.it("googleService.removeEventTiesToGoogle",{craftivity_calendar_id=local.options.craftivity_calendar_id}) />		

		<!--- drop the craftivity_google_calendar if exists --->
		<cfif local.options.craftivity_calendar_id neq "">
			<cfset $.run.it('googleService.deleteCraftivityCalendar') />
		</cfif>
		
		<!--- create the craftivity calendar in users's google account --->
		<cfset local.success = $.run.it('googleService.createCraftivityCalendar') />
		<!--- get calendars --->
		<cfset local.calendars = $.run.it('googleService.getCalendars') />
		<!--- get the calendarIDs --->
		<cfset local.google_calendar_ids = "" />
		<cfloop collection="#local.calendars#" item="local.item" index="local.i">
			<cfset local.google_calendar_ids = listAppend(local.google_calendar_ids,local.i) />
		</cfloop>
		<!--- save calendars to options --->
		<cfset local.options = $.run.it('optionService.saveOptions',{key="calendar",args={
			google_calendars=local.calendars,
			google_calendar_ids=local.google_calendar_ids
		}}) />
		
		<cfif local.success>
			<!--- get list of google calendars to sync with --->
			<cfset local.calendarIDs = $.run.it("googleService.getUserCalendars") />
			<cfset $.run.thread('googleService._pullAllEvents',{googleCalendarIDs=local.calendarIDs}) />
		<cfelse>
			<cfset local.options= $.run.it('optionService.saveOptions',{key='calendar',args={
				pullThread="Error getting Calendar"
			}}) />
		</cfif>
		
		<cfset $.database.save(
			table="user",
			id=request.session.user.id,
			force_google_sync=0
		) />
		
		<!--- invalidate the cache --->
		<cfset $.run.it('craftivitySErvice.getUser',{userID=request.session.user.id,invalidateCache=true}) />
		
		<!--- wait just a split fucking second for the thread to mark google sync as started --->
		<cfset sleep(500) />
		
		<cfreturn true />

	</cffunction>

	<!--- --->
	
	<!--- recursive and meant to run as thread --->
	<cffunction name="_pullAllEvents" access="public" output="false" returntype="string">
		<cfargument name="googleCalendarIDs" required="true" />
		<cfargument name="googleCalendarID" required="false" default="" />
		<cfargument name="nextPageToken" required="false" default="" />
		<cfargument name="threaded" required="false" default="true" />
		
		<cfsetting requestTimeOut="600" />

		<cfset var local = {} />
		
		<cfif not arguments.threaded>
			<cfset local.thread.name = "" />
		</cfif>
		
		<cfset local.options = $.run.it('optionService.getOptions',{key='calendar'}) />
		
		<!--- if first run, then load the first googleCalendarID --->
		<cfif arguments.googleCalendarID eq "">
			<cfset arguments.googleCalendarID = listGetAt(arguments.googleCalendarIDs,1) />
			<cfset arguments.googleCalendarIDs = listDeleteAt(arguments.googleCalendarIDs,1) />
			<cfset local.options = $.run.it('optionService.saveOptions',{key='calendar',args={
				pullThread=thread.name
			}}) />
		</cfif>
				
		<cfset local.package = $.run.it('googleService.getEventsByCalendarID',{
			calendarID=arguments.googleCalendarID,
			showDeleted="false",
			nextPageToken=arguments.nextPageToken
		}) />
		<!--- never allow same nextPageToken to be used twice --->
		<cfset arguments.nextPageToken = "" />
		
		<cfset local.formattedEvents = $.run.it('googleService.formatEventArray',{array=local.package.items,googleCalendarID=arguments.googleCalendarID}) />
		
		<!--- test for recurring event --->
		<cfloop collection="#local.formattedEvents#" item="local.thisGoogleEvent" index="local.thisGoogleEventID">
			<cfif structKeyExists(local.thisGoogleEvent,'recurrence') and arrayLen(local.thisGoogleEvent.recurrence) gt 0>
				<cfset local.result = $.run.it('googleService.saveRecurringEvent',{
					event=local.thisGoogleEvent
				}) />
			<!--- test for multiday event --->
			<cfelseif local.thisGoogleEvent.date neq local.thisGoogleEvent.stopdate>
				<cfset local.id = $.run.it(method='googleService.saveMultidayEvent',args={
					event=local.thisGoogleEvent,
					calendarID=local.thisGoogleEvent.google_calendar_id,
					googleEventID=local.thisGoogleEvent.id
				}) />
			<cfelse>
				<!--- update event to match google --->
				<cfset local.id = $.run.it(method='calendarService.saveEvent',args={
					id="",
					description=local.thisGoogleEvent.summary,
					start=local.thisGoogleEvent.starttime,
					stop=local.thisGoogleEvent.stoptime,
					date=local.thisGoogleEvent.date,
					category=0,
					google_calendar_id=arguments.googleCalendarID,
					google_event_id=local.thisGoogleEvent.id,
					google_calendar_link=local.thisGoogleEvent.google_calendar_link,
					ignore_google=true
				}) />
			</cfif>
		</cfloop>
		<!--- if nextPageToken call again with same arguments --->
		<cfif local.package.nextPageToken neq "">
			<cfset arguments.nextPageToken = local.package.nextPageToken />

			<cfreturn $.run.it('googleService._pullAllEvents',arguments) />
		<!--- if sync token, store and move on to next calendar --->
		<cfelseif local.package.nextSyncToken neq "">
			<!--- TODO: store syncToken to calendar --->
			<cfset local.options.google_calendars[arguments.googleCalendarID].nextSyncToken = local.package.nextSyncToken />
			<cfset local.options = $.run.it('optionService.saveOptions',{key='calendar',args={
				google_calendars=local.options.google_calendars}
			}) />
			<cfset arguments.googleCalendarID = "" />
				<cfif listLen(arguments.googleCalendarIDs) gt 0>
					<cfreturn $.run.it('googleService._pullAllEvents',arguments) />
				<cfelse>
					<cfset local.options= $.run.it('optionService.saveOptions',{key='calendar',args={
						pullThread=""
					}}) />
					<cfreturn $.run.it('googleService._pushEvents',{threaded=arguments.threaded}) />
				</cfif>
		</cfif>
		
		<cfreturn false />

	</cffunction>

	<!--- --->

</cfcomponent>